﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Caching;
using System.Globalization;

using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.Common.Utilities;

namespace Fuse8.DomainFramework.CacheService
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    public class TagHttpCacheService<TEntity, TKey> : BaseDependencyCacheService<TEntity, TKey>
        where TEntity : class, IEntityObject<TKey>, new()
        where TKey : IComparable<TKey>, IEquatable<TKey> 
    {
        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        public TagHttpCacheService()
        {
            CacheOptions = new CacheServiceOptions();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="options"></param>
        public TagHttpCacheService(CacheServiceOptions options)
            : this()
        {
            Guard.ArgumentNotNull(options, "options");

            if (options.AbsoluteTimeSpanExpiration != TimeSpan.Zero)
            {
                CacheOptions.AbsoluteTimeSpanExpiration = options.AbsoluteTimeSpanExpiration;
            }

            CacheOptions.UseAsyncMethods = options.UseAsyncMethods;
        }

        #endregion

        #region Override base members

        /// <summary>
        /// 
        /// </summary>
        public override IEnumerable<CacheItemView> CacheItems
        {
            get
            {
                lock (this)
                {
                    var enumerator = HttpRuntime.Cache.GetEnumerator();

                    while (enumerator.MoveNext())
                    {
                        yield return
                            new CacheItemView
                            {
                                Key = enumerator.Key.ToString(),
                                Value = enumerator.Value.ToString()
                            };
                    }
                }
            }
        }

        #endregion

        #region Override abstraction members

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TStored"></typeparam>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        protected override TStored ClientGet<TStored>(string cacheKey)
        {
            Guard.ArgumentNotEmpty(cacheKey, "cacheKey");

            return ((TStored)HttpRuntime.Cache.Get(cacheKey)).NormalClone();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TStored"></typeparam>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        protected override IDictionary<string, TStored> ClientGet<TStored>(IEnumerable<string> cacheKey)
        {
            Guard.ArgumentNotNull(cacheKey, "cacheKey");

            Dictionary<string, TStored> result = new Dictionary<string, TStored>();

            foreach (var item in cacheKey)
            {
                var cachedItem = (TStored)HttpRuntime.Cache.Get(item);
                if (cachedItem != null)
                {
                    if (result.ContainsKey(item))
                    {
                        result[item] = cachedItem;
                    }
                    else
                    {
                        result.Add(item, cachedItem);
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TStored"></typeparam>
        /// <param name="mode"></param>
        /// <param name="cacheKey"></param>
        /// <param name="cachedObject"></param>
        /// <param name="absoluteExpiration"></param>
        /// <param name="slidingExpiration"></param>
        /// <returns></returns>
        protected override bool ClientStore<TStored>(CacheStoreMode mode, string cacheKey, TStored cachedObject, DateTime absoluteExpiration, TimeSpan slidingExpiration)
        {
            Guard.ArgumentNotEmpty(cacheKey, "cacheKey");
            Guard.ArgumentNotNull(cachedObject, "cachedObject");

            HttpRuntime.Cache.Insert(
                    cacheKey,
                    cachedObject.NormalClone(),
                    null,
                    slidingExpiration == Cache.NoSlidingExpiration ?
                        absoluteExpiration :
                        Cache.NoAbsoluteExpiration,
                    absoluteExpiration == Cache.NoAbsoluteExpiration ?
                        slidingExpiration :
                        Cache.NoSlidingExpiration,
                    CacheOptions.Priority,
                    CacheOptions.RemovedCallback);

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TStored"></typeparam>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        protected override bool ClientRemove<TStored>(string cacheKey)
        {
            Guard.ArgumentNotEmpty(cacheKey, "cacheKey");

            object removedItem = HttpRuntime.Cache.Remove(cacheKey);

            return removedItem != null;
        }

        /// <summary>
        /// 
        /// </summary>
        protected override void ClientFlushAll()
        {
            var cacheItems = CacheItems;

            lock (this)
            {
                foreach (var item in cacheItems)
                {
                    HttpRuntime.Cache.Remove(item.Key);
                }
            }
        }

        #endregion
    }
}
